iT邦幫忙

2023 iThome 鐵人賽

DAY 24
1

cover

在接下來的的幾天內,需要跟 API server 去整合跟修正使用真實資料後的調整,在這邊會使用到了 React Hook 來整合我們的 Request API 的資料流。

使用 React Hook

首先,在一般的 API Request 有著重複的使用場景:

  1. API request 的執行狀態。
  2. 處理在 Request 執行期間的任何錯誤。
  3. 處理回傳的 Body

所以我先建立了一個 hook useApiRequest

import {useCallback, useState} from "react";

export default function useApiRequest(apiFunction) {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [responseData, setResponseData] = useState(null);

  const fetchAPI = useCallback(
      async (...args) => {
        try {
          setIsLoading(true);
          const result = await apiFunction(...args);

          setResponseData(result);
          setIsLoading(false);
          return result;

        } catch (e) {
          setError(e);
          setIsLoading(false);
          throw e;
        }
      },
      [apiFunction]
  );

  return {isLoading, fetchAPI, responseData, error };
};
import axios from 'axios';
import {useAuthContext} from "@/context/AuthContext";

const apiClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_HOST
});

apiClient.interceptors.request.use(async (config) => {
  const {user} = useAuthContext();

  if (user !== null) {
    const token = await user.getIdToken();
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
}, (error) => {
  return Promise.reject(error);
});

export default apiClient;

在這邊主要有幾個部分:

  • useState:我們使用了三個狀態:
    • isLoading:確認 API request 是否正在執行。
    • error:Axios 回傳的 Error。
    • responseData:處理回傳的 body data。
  • useCallback:使用 useCallback 定義 fetchAPI 函數,確認不會每次 render 時重新建立。
  • apiFunction:這個 hook 需要傳入 API request 的 function。
  • 回傳值:這邊會回傳上面所使用的參數。

特定 endpoint 的 function 和 Hooks

雖然 useApiRequest hook 提供了一種通用的方式來執行 API request,但我們仍然需要設定分別的 API endpoint request。

import useApiRequest from "@/api/base";
import apiClient from "@/api/client";

// get serverHost from env
const apiURL = `${process.env.NEXT_PUBLIC_API_HOST}/api`;

export const useGetChannels = () => {
  const getChannels = async (options) => {
    const response = await apiClient.GET(`${apiURL}/channels`, options);
    return response.json();
  };

  return useApiRequest(getChannels);
};

export const useGetChannelById = () => {
  const getChannelById = async (channelId, options) => {
    const response = await apiClient.GET(`${apiURL}/channels/${channelId}`, options);
    return response.json();
  };

  return useApiRequest(getChannelById);
};

export const useGetEpisodeById = () => {
  const getEpisodeById = async (episodeId, options) => {
    const response = await apiClient.GET(`${apiURL}/episodes/${episodeId}`, options);
    return response.json();
  };

  return useApiRequest(getEpisodeById);
};


在這邊有幾個部分:

  • 環境變數:NEXT_PUBLIC_API_HOST 來自於環境變數,這樣可以預留出不同環境(如開發、測試、生產)去切換的靈活性。
  • Endpoint function:像 getChannelById 為例,會直接回傳 response 的 json。
  • Endpoint Hooks:像 useGetChannelById 為例,把 getChannelById 再經過 useApiRequest hook 的包裝。

在 page 中使用

在需要的 page 中使用 API 資料只需要引入對應的 hook 就可以。

import {useGetChannels} from '@/api/all';
// ..
const {responseData, fetchAPI} = useGetChannels();

useEffect(() => {
  if (channelSlug && channelSlug.length > 0) {
    fetchAPI(responseData);
  }
}, [channelSlug, fetchAPI]);

只需要簡單檢查一下 channelSlug 是否有效,就可以很簡單達成從 API 取得資料的流程。

Referrences


上一篇
#22 面向使用者的第一線:部署 Production-Ready 的 Web App
下一篇
#24 讓 Notification 時時刻刻聯繫你的使用者 (1/2)
系列文
Laravel 擴展宇宙:從 1 到 100 十倍速打造產品獨角獸30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言